#ifndef IINPUTSYSTEM_H_INCLUDED
#define IINPUTSYSTEM_H_INCLUDED

#include "GLOBAL.h"

/// Mouse key enumeration
enum PIE_MOUSE {
	LEFT_BUTTON = 0,
	RIGHT_BUTTON,
	MIDDLE_BUTTON,
	BUTTON_4,
	BUTTON_5,
	BUTTON_6,
	BUTTON_7,
	BUTTON_8
};

#define PRESSED 	1
#define HELD    	2
#define RELEASED 	4

/// Input sub-device state ( like a button, thumbstick etc.. )
/// Currently not used because mouse and keyboard are integrated into the main IInputSystem
struct sInputState {
	// Data:
	bool m_Released;					///< Controller state released
	bool m_Pressed;					///< Controller state pressed
	bool m_Clicked;					///< Controller state clicked
	real m_Value;						///< Controller value 1
	bool m_Connected;					///< Controller Is Connected

	real m_PressedMilliseconds;	///< Pressed for this time
	real m_ReleasedMilliseconds;	///< Released for this time
	real m_ClickedMilliseconds;	///< Clicked this time ago
};

/// Abstract Input Device I.E. Keyboard/ Mouse/...
/// Currently not used because mouse and keyboard are integrated into the main IInputSystem
class IInputDevice {
public:
	// Data
	str		m_strName;			///< Name of the input device
	uint		m_uiControllerID;	///< Contoller ID
   sInputState* m_pState;		///< Input state

	void UpdateState( real deltatime, bool connected, real value ) {
		m_pState->m_Pressed	= abs(value) > EPS;
		m_pState->m_Clicked	= m_pState->m_Released && m_pState->m_Pressed;
		m_pState->m_Released = !m_pState->m_Pressed;
		m_pState->m_Value 	= value;
		m_pState->m_PressedMilliseconds	= m_pState->m_Pressed  ? m_pState->m_PressedMilliseconds + deltatime : 0.0f;
		m_pState->m_ReleasedMilliseconds = m_pState->m_Released ? m_pState->m_ReleasedMilliseconds + deltatime : 0.0f;
		m_pState->m_ClickedMilliseconds	= m_pState->m_Clicked  ? 0 : m_pState->m_ClickedMilliseconds + deltatime;
		m_pState->m_Connected = connected;
	}

public:
	// Functions:
	virtual void Update( creal &dt ) = 0;

	  //C&D
	  IInputDevice( uint controllerid, str name ) : m_strName( name ), m_uiControllerID(controllerid) {
	  }
	 virtual ~IInputDevice( ) {
	  }
};

/// The Input Access Interface
class IInputSystem {
protected:
	// Protected data:
	list<IInputDevice*> m_listDevices;	///< List of registered device detectors


	bool	m_bInitialized;	///< Is input system initialized
	long	m_lLastMouseX;		///< Last known Mouse X coord
	long	m_lLastMouseY;		///< Last known Mouse Y coord
	long	m_lMouseXRel;		///< Relative mouse movement along X
	long	m_lMouseYRel;		///< Relative moues movement along Y
	long	m_lButtons;			///< Number of mouse buttons
	long	m_lMouseX;			///< Mouse X coord
	long	m_lMouseY;			///< Mouse Y coord
	long	m_lAxes; 			///< Mouse axes of movement

public:
	// Get functions:
	real GetReleasedMilliseconds	( str name, bool invalidate = false );
	real GetPressedMilliseconds	( str name, bool invalidate = false );
	real GetClickedMilliseconds	( str name, bool invalidate = false );

	bool GetReleased	( str name, bool invalidate = false );
	bool GetPressed	( str name, bool invalidate = false );
	bool GetClicked	( str name, bool invalidate = false );
	real GetValue		( str name, bool invalidate = false );

	virtual byte IsKeyboardButtonDownDirect( uint button ) = 0;
	virtual byte IsMouseButtonDownDirect	( uint button ) = 0;

	long GetLastMouseX( ) const { return m_lLastMouseX; }
	long GetLastMouseY( ) const { return m_lLastMouseY; }
	long GetRelMouseX	( ) const { return m_lMouseXRel; }
	long GetRelMouseY	( ) const { return m_lMouseYRel; }
	long GetMouseX		( ) const { return m_lMouseX; }
	long GetMouseY		( ) const { return m_lMouseY; }

	// Public functions:
	/// Bind the input receiver to a specific window
	virtual bool Bind			( handle hwnd ) = 0;
	/// Initialize the input devices and subdivices
	virtual bool Initialize	( ) = 0;
	/// Update all input
	virtual void Update		( creal &gameTime ) {
		for( list<IInputDevice*>::iterator it = m_listDevices.begin(); it != m_listDevices.end(); it++ )
			(*it)->Update( gameTime );
	}
	/// Add new device
	void Add			( IInputDevice* binding ) {
		m_listDevices.push_back( binding );
	}
	/// Check if device has been added
	bool Contains	( str name ) {
		for( list<IInputDevice*>::iterator it = m_listDevices.begin(); it != m_listDevices.end(); it++ ) {
			if( (*it)->m_strName == name )
				return true;
		}
		return false;
	}
	/// Clear all devices
	void Clear		( ) {
		m_listDevices.clear( );
	}

	//pieInputState* GetState( str name, bool invalidate = false ) { for( list<pieAInputDevice*>::iterator it = m_listDevices.begin(); it != m_listDevices.end(); it++ ) { if( (*it)->m_strName == name ) return (*it); } return NULL; }
	bool IsInitialized( ) const { return m_bInitialized; }

	 // C&D
	 IInputSystem( )
	 : m_bInitialized(false),
		m_lLastMouseX(0), m_lLastMouseY(0), m_lMouseXRel(0), m_lMouseYRel(0), m_lButtons(0), m_lMouseX(0), m_lMouseY(0), m_lAxes(0) { }
	virtual ~IInputSystem( ) { }
};

#endif // IINPUTSYSTEM_H_INCLUDED
